home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-02 | 19.8 KB | 538 lines | [TEXT/CCL2] |
- ;;; An attribute-value parser.
- ;;; Graphics are provided in a separate file.
-
-
- ;;; Features
-
- (defvar *AV-Generation* 0) ; the generation counter
-
- ;;; if the current generation is generation then the value of the avnode
- ;;; is old-contents, otherwise it is new-contents.
-
- (defstruct avnode oldcontents newcontents generation)
-
- (defmacro New-Generation ()
- "Increment the global generation counter"
- `(incf *AV-Generation*))
-
- (defmacro UpToDate (avnode)
- "T if this node's generation is EQ to the global generation counter"
- `(eq *AV-Generation* (avnode-generation ,avnode)))
-
- (defmacro avnode-contents (avnode)
- "The current contents of this node"
- `(if (avnode-p ,avnode)
- (if (UpToDate ,avnode)
- (avnode-newcontents ,avnode)
- (avnode-oldcontents ,avnode))
- ,avnode))
-
- ;;; type predicates on contents
-
- (defmacro pointer-p (contents)
- "T if contents are another avnode"
- `(avnode-p ,contents))
-
- (defmacro constant-p (contents)
- "T if contents are a constant"
- `(and ,contents (symbolp ,contents)))
-
- (defmacro variable-p (contents)
- "T if contents is uninstantiated"
- `(null ,contents))
-
- (defmacro complex-p (contents)
- "T if contents are a list of AV pairs"
- `(consp ,contents))
-
- ;;; macros for manipulating the av-pairs of complex nodes
-
- (defmacro avpair-att (avp)
- "The attribute of an AV pair"
- `(car ,avp))
-
- (defmacro avpair-val (avp)
- "The value of an AV pair"
- `(cdr ,avp))
-
- (defmacro make-avpair (att val)
- "AV pair constructor"
- `(cons ,att ,val))
-
- (defmacro avpairs-att-val (avps att)
- "The value of the att attribute of avps"
- `(cdr (assoc ,att ,avps)))
-
- (defmacro smash-to-pointer (avn1 avn2)
- "Makes avn1 point to avn2"
- `(progn (setf (avnode-generation ,avn1) *AV-Generation*)
- (setf (avnode-newcontents ,avn1) ,avn2)))
-
- (defmacro copy-contents (avn)
- "Replaces avn's newcontents with a copy of its oldcontents"
- `(progn (setf (avnode-generation ,avn) *AV-Generation*)
- (setf (avnode-newcontents ,avn) (copy-list (avnode-oldcontents ,avn)))))
-
- ;;; useful functions on avnodes
-
- (defun follow-pointers (avnode)
- "Follows a chain of pointer nodes until a non-pointer is reached"
- (if (avnode-p avnode)
- (let ((avnode-contents (avnode-contents avnode)))
- (if (or (pointer-p avnode-contents)
- (constant-p avnode-contents))
- (follow-pointers avnode-contents)
- avnode))
- avnode))
-
- (defun avn-att-val (avnode att)
- "Returns the value of avnode's att attribute, follows pointers"
- (let ((contents (avnode-contents (follow-pointers avnode))))
- (if (complex-p contents)
- (avpairs-att-val contents att)
- nil)))
-
- (defun make-att-val (avnode att)
- "Gets the value of avnode's att attribute, constructing such a node if necessary.
- Returns NIL in case of failure."
- (let* ((node (follow-pointers avnode))
- (contents (avnode-contents node)))
- (if (or (complex-p contents)
- (variable-p contents))
- (let ((val (avpairs-att-val contents att)))
- (if val
- val
- (let ((new-node (make-avnode)))
- (unless (UpToDate node)
- (Copy-Contents node))
- (if (avnode-newcontents node) ; add to end of av-list
- (setf (cdr (last (avnode-newcontents node))) (list (cons att new-node)))
- (push (cons att new-node) (avnode-newcontents node)))
- new-node)))
- 'nil)))
-
- (defun unify-avs (av1 av2)
- (let ((nav1 (follow-pointers av1)) ; follow all pointer nodes
- (nav2 (follow-pointers av2)))
- (if (eq nav1 nav2)
- nav1
- (let ((cav1 (avnode-contents nav1)) ; get associated contents
- (cav2 (avnode-contents nav2)))
- (cond ((variable-p cav1) (smash-to-pointer nav1 nav2)) ; smash variables
- ((variable-p cav2) (smash-to-pointer nav2 nav1))
- ((or (constant-p cav1) (constant-p cav2)) nil) ; no consts
- (t (smash-to-pointer nav1 nav2) ; handle complex
- (unless (UpToDate nav2)
- (copy-contents nav2))
- (if (every #'(lambda (avp)
- (setf nav2 (follow-pointers nav2))
- (let ((node (avpairs-att-val (avnode-newcontents nav2)
- (avpair-att avp))))
- (if node
- (unify-avs (avpair-val avp) node)
- (push avp (avnode-newcontents nav2)))))
- cav1)
- nav2
- 'nil)))))))
-
- (defun subsume-avs-p (avs1 avs2) ; t if avs1 is more general than avs2
- (labels
- ((sub-avs (av1 av2) ; subsumption differs from unification in that
- (if (eq av1 av2) ; only the variables of av1 may be bound, and
- av1 ; these may only be bound once!
- (let ((cav1 (avnode-contents av1)) ; get associated contents
- (cav2 (avnode-contents av2)))
- (cond ((pointer-p cav1) (eq (follow-pointers av1) av2))
- ((variable-p cav1) (smash-to-pointer av1 av2)) ; smash variables
- ((variable-p cav2) nil)
- ((or (constant-p cav1) (constant-p cav2)) nil) ; no consts
- (t (smash-to-pointer av1 av2) ; complex
- (if (every #'(lambda (avp)
- (sub-avp (avpair-att avp)
- (avpair-val avp)
- cav2))
- cav1)
- av2
- nil))))))
- (sub-avp (att val avpairs)
- (if avpairs
- (if (eq att (avpair-att (first avpairs)))
- (sub-avs val (avpair-val (first avpairs)))
- (sub-avp att val (rest avpairs)))
- nil))
- )
- (sub-avs avs1 avs2)))
-
- ;;; the idea is that we use new-contents as a scratch field into
- ;;; which we stuff the copy of the current node. We indicate we have
- ;;; done this by using a new copy generation.
-
- (defvar *Copy-Generation* (list '*copy*)) ; The copy generation
-
- (defun Reset-Copier () (setq *Copy-Generation* (list '*copy*)))
-
- (defun copy-avs (avs-node)
- (let ((node (follow-pointers avs-node))) ; follow any pointers
- (if (constant-p node)
- node
- (if (eq (avnode-generation node) *Copy-Generation*); this node has a copy
- (avnode-newcontents node) ; return its copy
- (let ((contents (avnode-contents node)) ; get the contents
- (new-node (make-avnode))) ; before we clobber them
- (setf (avnode-generation node) *Copy-Generation*); stick copy node on
- (setf (avnode-newcontents node) new-node) ; to node
- (setf (avnode-oldcontents new-node)
- (mapcar #'(lambda (avp)
- (make-avpair (avpair-att avp)
- (copy-avs (avpair-val avp))))
- contents))
- new-node)))))
-
- (defun restrict-avs (avs restrictor)
- (let ((gen (list '*restrictor-generation*)))
- (labels ((doNode (avs-node res-node)
- (let ((a (follow-pointers avs-node)) ; follow any pointers
- (r (follow-pointers res-node)))
- (if (constant-p a) ; constants are never restricted
- a
- (if (eq (avnode-generation a) gen) ; this node has a copy
- (avnode-newcontents a) ; return its copy
- (let ((a-contents (avnode-oldcontents a)) ; get the contents
- (new-node (make-avnode))) ; before we clobber them
- (setf (avnode-generation a) gen) ; stick copy node on
- (setf (avnode-newcontents a) new-node) ; to node
- (if (complex-p (avnode-contents r))
- (setf (avnode-oldcontents new-node)
- (doAvpairs a-contents (avnode-oldcontents r))))
- new-node)))))
- (doAvpairs (apairs rpairs)
- (if apairs
- (let ((rpair (assoc (avpair-att (first apairs)) rpairs)))
- (if rpair
- (cons (make-avpair (avpair-att rpair)
- (doNode (avpair-val (first apairs))
- (avpair-val rpair)))
- (doAvpairs (rest apairs) rpairs))
- (doAvpairs (rest apairs) rpairs)))
- '())))
- (New-Generation)
- (Reset-copier)
- (doNode avs restrictor))))
-
- (defvar *name-bindings* nil) ; a global name-bindings variable used only by
- ; avs-to-list
-
- (defun Reset-List-To-Avs () (setq *name-bindings* nil))
-
- (defun list-to-avs (avs-data)
- (labels ((var-sym (x)
- (let ((s (string x)))
- (char= (elt s (1- (length s))) #\?)))
- (lookup (name)
- (follow-pointers (cdr (assoc name *name-bindings*))))
- (store (name value)
- (push (cons name value) *name-bindings*)
- value)
- (build (data)
- (cond ((and (symbolp (first data)) ;; constant
- (not (var-sym (first data))))
- (first data))
- ((symbolp (first data)) ;; variable
- (or (lookup (first data))
- (let ((new-node (make-avnode)))
- (store (first data) new-node)
- (setf (avnode-oldcontents new-node)
- (mapcar #'(lambda (p)
- (make-avpair (car p)
- (build (cdr p))))
- (rest data)))
- new-node)))
- (t
- (make-avnode :oldcontents
- (mapcar #'(lambda (p)
- (make-avpair (car p)
- (build (cdr p))))
- data))))))
- (build avs-data)))
-
- (defun avs-to-list (avs)
- (let ((copy-generation (list '*list-copy*)))
- (labels ((doNode (avs-node)
- (let ((node (follow-pointers avs-node)))
- (if (constant-p node)
- (list node)
- (if (eq (avnode-generation node) copy-generation)
- (avnode-newcontents node)
- (let ((contents (avnode-contents node))
- (new-node (list '?)))
- (setf (avnode-generation node) copy-generation)
- (setf (avnode-newcontents node) new-node)
- (let ((daughters
- (mapcar #'(lambda (avp)
- (make-avpair (avpair-att avp)
- (doNode (avpair-val avp))))
- contents)))
- (when daughters
- (setf (car new-node) (car daughters))
- (setf (cdr new-node) (cdr daughters)))
- new-node)))))))
- (doNode avs))))
-
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;; AVG
-
- ;;; A grammar exports the following functions
- ;;;
- ;;; (ExpandMother g cat)
- ;;; (LeftDaughter g rule)
- ;;; (ShiftRule g rule cat)
- ;;; (ReduceRule g rule)
- ;;; (SubsumeCat g cat1 cat2)
- ;;; (UnifyCat g cat1 cat2)
- ;;; (RestrictCat g cat)
- ;;; (Lexicon g word)
- ;;; (StartCat g)
-
-
- (defun rules (grammar) (first grammar))
- (defun lex-forms (grammar) (second grammar))
- (defun start (grammar) (third grammar))
- (defun restrictor (grammar) (fourth grammar))
-
- (defstruct rule mother daughters) ; a dotted rule
-
- (defun BuildGrammar (grammar-rules lexicon start restriction)
- (list (mapcar #'(lambda (r)
- (Reset-list-to-avs) ; reset all the lexical bindings
- (make-rule :mother (list-to-avs (first r))
- :daughters (mapcar #'list-to-avs
- (rest (rest r)))))
- grammar-rules)
- (mapcar #'(lambda (lex)
- (cons (first lex)
- (mapcar #'(lambda (l)
- (Reset-list-to-avs)
- (list-to-avs l))
- (rest lex))))
- lexicon)
- (progn (Reset-list-to-avs)
- (list-to-avs start))
- (progn (Reset-list-to-avs)
- (list-to-avs restriction))))
-
- (defun ExpandMother (g cat)
- (mapcan #'(lambda (r)
- (New-Generation)
- (when (Unify-avs (rule-mother r) cat)
- (Reset-Copier)
- (list (make-rule :mother (Copy-avs (rule-mother r))
- :daughters (mapcar #'Copy-avs
- (rule-daughters r))))))
- (rules g)))
-
- (defun LeftDaughter (g rule)
- (declare (ignore g))
- (first (rule-daughters rule)))
-
- (defun ShiftRule (g rule cat)
- (let ((first-daughter (LeftDaughter g rule)))
- (when (and first-daughter
- (New-Generation)
- (Unify-avs first-daughter cat))
- (Reset-Copier)
- (make-rule :mother (Copy-avs (rule-mother rule))
- :daughters (mapcar #'Copy-avs
- (rest (rule-daughters rule)))))))
-
- (defun ReduceRule (g rule)
- (declare (ignore g))
- (if (null (rule-daughters rule))
- (rule-mother rule)))
-
- (defun SubsumeCat (g cat1 cat2)
- (declare (ignore g))
- (New-Generation)
- (Subsume-avs-p cat1 cat2))
-
- (defun RestrictCat (g cat)
- (Restrict-avs cat (restrictor g)))
-
- (defun UnifyCat (g cat1 cat2)
- (declare (ignore g))
- (New-Generation)
- (Unify-avs cat1 cat2))
-
- (defun StartCat (g)
- (third g))
-
- (defun Lexicon (g w)
- (let ((categories (cdr (assoc w (lex-forms g)))))
- (unless categories
- (format t "Warning: The word ~s does not appear in the lexicon!~%" w))
- categories))
-
- (defun CatPrintForm (g cat)
- (declare (ignore g))
- (avs-to-list cat))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;; Chart
-
- (defstruct search rule parent daughters)
- (defstruct goal cat loc instances parents)
- (defstruct inst cat loc daughters)
- (defstruct lex cat loc word)
-
- (defvar *database* nil)
- (defvar *lexical-database* nil)
-
- (defvar *g*) ; the grammar. This must be bound elsewhere!
-
- (defvar *tracer* '()) ; list of places where tracing takes place.
-
- (defun Seek (cat from parent)
- (let* ((old-goal (lookup-goal cat from)))
- (if old-goal
- (progn
- (push parent (goal-parents old-goal))
- (dolist (i (goal-instances old-goal))
- (inform parent i))
- old-goal)
- (let* ((r-cat (RestrictCat *g* cat))
- (new-goal (create-goal r-cat from parent)))
- (if (member 'seek *tracer*)
- (format t "Looking for ~s at location ~s~%"
- (CatPrintForm *g* r-cat) from))
- (CheckLex new-goal)
- (dolist (r (ExpandMother *g* r-cat))
- (process-rule r from new-goal nil))
- new-goal))))
-
- (defun inform (s i)
- (let ((new-rule (ShiftRule *g* (search-rule s) (inst-cat i))))
- (if new-rule
- (process-rule new-rule (inst-loc i) (search-parent s)
- (cons i (search-daughters s))))))
-
- (defun process-rule (rule loc parent daughters)
- (let ((daughter (LeftDaughter *g* rule)))
- (if daughter
- (Seek daughter loc (make-search :rule rule :parent parent
- :daughters daughters))))
- (let ((mother (ReduceRule *g* rule)))
- (if mother
- (found parent mother loc (reverse daughters)))))
-
- (defun found (goal cat loc daughters)
- (if (member 'found *tracer*)
- (format t "Found ~s from ~s to ~s~%"
- (CatPrintForm *g* cat) (goal-loc goal) loc))
- (let ((new-instance (make-inst :cat cat :loc loc :daughters daughters)))
- (push new-instance (goal-instances goal))
- (dolist (s (goal-parents goal))
- (inform s new-instance))))
-
- (defun Create-goal (cat loc parent)
- (let ((new-goal (make-goal :cat cat
- :loc loc
- :parents (if parent (list parent)))))
- (push new-goal *database*)
- new-goal))
-
- (defun Lookup-goal (cat loc)
- (find-if #'(lambda (g)
- (and (= (goal-loc g) loc)
- (SubsumeCat *g* (goal-cat g) cat)))
- *database*))
-
- (defun chart (words)
- (setq *database* '())
- (setq *lexical-database* '())
- (LoadWords words 0)
- (mapcar #'(lambda (i)
- (list (InstToTreeList i)
- (CatPrintForm *g* (inst-cat i))))
- (goal-instances (Seek (Startcat *g*) 0 nil))))
-
- (defun LoadWords (words loc)
- (when words
- (dolist (cat (Lexicon *g* (first words)))
- (AddWord cat loc (first words)))
- (LoadWords (rest words) (+ 1 loc))))
-
- (defun AddWord (cat loc word)
- (push (make-lex :cat cat :loc loc :word word) *lexical-database*)
- (dolist (g *database*)
- (if (and (= (goal-loc g) loc)
- (UnifyCat *g* (goal-cat g) cat))
- (found g cat (1+ loc) nil))))
-
- (defun CheckLex (goal)
- (dolist (l *lexical-database*)
- (if (and (= (lex-loc l) (goal-loc goal))
- (UnifyCat *g* (goal-cat goal) (lex-cat l)))
- (found goal (lex-cat l) (1+ (lex-loc l)) (list (lex-word l))))))
-
- (defparameter *cat-prefix* 'cat "the prefix to follow to find the category label")
-
- (defun InstToTreeList (inst)
- "builds a conventional phrase structure tree for this inst"
- (labels ((label (i)
- (let ((c (if *cat-prefix*
- (avn-att-val (inst-cat i) *cat-prefix*)
- (inst-cat i))))
- (if (symbolp c)
- c
- (avs-to-list c)))))
- (if (inst-p inst)
- (if (inst-daughters inst)
- (cons (label inst)
- (mapcar #'InstToTreeList (inst-daughters inst)))
- (label inst))
- inst)))
-
- (defvar *results* '()) ; where the results of the last computation are stored
-
- (defun parse (words)
- (setq *database* '())
- (setq *lexical-database* '())
- (LoadWords words 0)
- (let ((len (length words))
- (start (Startcat *g*)))
- (setq *results*
- (remove-if-not
- #'(lambda (i)
- (and (= (inst-loc i) len)
- (UnifyCat *g* start (inst-cat i))))
- (goal-instances (Seek start 0 nil)))))
- (New-Generation) ; ensure that there are no false bindings!
- (format t "There are ~s results~%" (length *results*))
- (when *results*
- (Display 1)
- (format t "~%Enter (Display n) to see other results")))
-
- (defmacro p (&rest words)
- `(parse ',words))
-
- #|
- (defun Display (n)
- (if (<= 1 n (length *results*))
- (let ((e (elt *results* (- n 1)))
- (*print-pretty* t)
- (*print-circle* t))
- (format t "~%Result ~a~%" n)
- (format t "~%~a~%" (InstToTreeList e))
- (format t "~%~a~%" (avs-to-list (if *val-prefix*
- (make-att-val (inst-cat e) *val-prefix*)
- (inst-cat e)))))
- (format t "Sorry, there are only ~s results~%" (length *results*))))
- |#
-
- (defun Instance-Count ()
- "Counts the number of instances found during the last parse"
- (apply #'+ (mapcar #'(lambda (g)
- (length (goal-instances g))) *database*)))